﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;

namespace SmartMeterParamFrame
{
    public class EncryptionMachine : IDisposable
    {
        /// <summary>
        /// 公钥分散因子
        /// </summary>
        private const string CONST_PUBLICDVI = "0000000000000001";
        /// <summary>
        /// 加密机配置文件名称
        /// </summary>
        private const string CONST_CONFIG = "MachineConfig.xml";
        /// <summary>
        /// 记录文件夹名称（主要用于存放密钥下装时3类密钥文本）
        /// </summary>
        //private const string CONST_lOGFLORDER = @"\KeyLog";

        /// <summary>
        /// 加密狗密码
        /// </summary>
        private static string m_Pwd = "11111111";

        /// <summary>
        /// 当前对象实例数量
        /// </summary>
        private static int objectCount = 0;

        private EncryptionMachine()
        {

        }

        /// <summary>
        /// 初始化基本参数
        /// </summary>
        private static void Init()
        {
            string path = string.Format(@"{0}\{1}", System.Environment.CurrentDirectory, CONST_CONFIG);
            XDocument xDoc = new XDocument();
            if (!File.Exists(path))
            {
                xDoc.Add(new XElement("Config", new XElement("Item", new XAttribute("Name", "UsbPwd"), new XAttribute("Value", m_Pwd))));
                xDoc.Save(path);
            }
            else
            {
                try
                {
                    xDoc = XDocument.Load(path);

                    m_Pwd = xDoc.Element("Config").Descendants().Where(x => x.Attribute("Name").Value == "UsbPwd").First().Attribute("Value").Value;

                }
                catch
                {
                    if (xDoc.Element("Config") == null) xDoc.Add(new XElement("Config"));

                    xDoc.Element("Config").Add(new XElement("Item", new XAttribute("Name", "UsbPwd"), new XAttribute("Value", m_Pwd)));

                    xDoc.Save(path);
                }
            }

            //try
            //{
            //    string logdir = string.Format(@"{0}\{1}", System.Environment.CurrentDirectory, CONST_lOGFLORDER);

            //    if (System.IO.Directory.Exists(logdir))
            //    {
            //        System.IO.Directory.Delete(logdir, true);
            //    }
            //}
            //catch { }

        }


        /// <summary>
        /// 连接加密机
        /// </summary>
        /// <param name="IP">加密机IP地址</param>
        /// <param name="Port">加密机端口</param>
        /// <param name="Ctime">超时时间</param>
        /// <returns></returns>
        public static EncryptionMachine LinkMachine(string IP, string Port, string Ctime)
        {
            if (objectCount > 0) throw new Exception("已经存在一个连接实例，不允许创建多个");

            objectCount++;
#if !DEBUG
            EncryptionMachine machine = null;

            Init();

            try
            {

                if (API.OpenUsbkey() != 0)
                {
                    objectCount--;
                    return machine;
                }
            }
            catch (Exception e) { objectCount--; throw e; }

            StringBuilder ip = new StringBuilder(IP);

            ushort sPort = 0;

            try
            {
                sPort = ushort.Parse(Port);
            }
            catch (Exception e) { objectCount--; throw e; }

            int intPwdLen = m_Pwd.Length;

            StringBuilder sPwd = new StringBuilder(m_Pwd);

            try
            {
#if PRIVATEKEY
                byte[] bytsecurity = Encoding.GetEncoding("GB2312").GetBytes("20100915国网计量中心");

                IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(bytsecurity.Length);

                System.Runtime.InteropServices.Marshal.Copy(bytsecurity, 0, ptr, bytsecurity.Length);

                machine = API.LgServer(ip, sPort, intPwdLen, sPwd, ptr) == 0 ? new EncryptionMachine() : null;
                Marshal.FreeHGlobal(ptr);
#else

                machine = API.LgServer(ip, sPort, intPwdLen, sPwd) == 0 ? new EncryptionMachine() : null;

#endif
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                if (machine == null) objectCount--;
            }
#else
            EncryptionMachine machine = new EncryptionMachine();
#endif
            return machine;
        }


        /// <summary>
        /// 登出服务器
        /// </summary>
        public void LgoutMachine()
        {
            try
            {
#if !DEBUG
                lock (this)
                {
                    API.LgoutServer();

                    API.ClseUsbkey();
                }
#endif
            }
            catch { }

        }


        /// <summary>
        /// 远程身份认证
        /// </summary>
        /// <param name="KeyState">密钥状态</param>
        /// <param name="meterID">表号（非表地址）</param>
        /// <param name="OutRand">输出随机数</param>
        /// <param name="OutEndata">输出密文</param>
        /// <returns></returns>
        public bool IdentityAuthentication(EnumKeyState KeyState, string meterID, out string OutRand, out string OutEndata)
        {
            OutRand = OutEndata = "";

            StringBuilder Div = new StringBuilder(KeyState == EnumKeyState.公钥状态 ? CONST_PUBLICDVI : meterID.PadLeft(16, '0'));

            StringBuilder m_OutRand = new StringBuilder();

            StringBuilder m_OutEndata = new StringBuilder();

            StringBuilder m_Nameid = new StringBuilder("szclou");

            int result = 1;

            lock (this)
            {
                result = API.IdentityAuthentication((int)KeyState, Div, m_OutRand, m_OutEndata, m_Nameid);  //远程身份认证
            }

            if (result != 0) return false;

            OutRand = m_OutRand.ToString().Replace("\0", "");
            OutEndata = m_OutEndata.ToString().Replace("\0", "");

            return true;
        }

        /// <summary>
        /// 获取清零密文
        /// </summary>
        /// <param name="KeyState">密钥状态</param>
        /// <param name="meterID">表号</param>
        /// <param name="MeterRand">表随机数</param>
        /// <param name="MeterEsamSerial">表ESAM序列号</param>
        /// <param name="PutKey">远程控制密钥密文信息明文</param>
        /// <param name="OutKey">输出Key</param>
        /// <param name="OutMac">输出MAC</param>
        /// <returns></returns>
        public bool getClearKey(EnumKeyState KeyState, string meterID, string MeterRand, string MeterEsamSerial, string PutKey, out string OutKey, out string OutMac)
        {
            OutMac = OutKey = "";

            StringBuilder Div = new StringBuilder(KeyState == EnumKeyState.公钥状态 ? CONST_PUBLICDVI : meterID.PadLeft(16, '0'));

            StringBuilder outMAC = new StringBuilder();        //输出的MAC

            byte[] outKey = new byte[80];      //输出的密文

            StringBuilder rand = new StringBuilder(MeterRand);

            StringBuilder esam = new StringBuilder(MeterEsamSerial);

            StringBuilder putKey = new StringBuilder(PutKey == "" ? "".PadLeft(8, '0') : PutKey);

            int result = 1;

            lock (this)
            {
                result = API.ClearKeyInfo((int)KeyState, rand, Div, esam, putKey, outKey, outMAC);
            }
            if (result != 0) return false;

            OutKey = Encoding.UTF8.GetString(outKey).Substring(0, 64);

            OutMac = outMAC.ToString().Replace("\0", "");

            return true;
        }

        /// <summary>
        /// 二类参数设置
        /// </summary>
        /// <param name="ks">密钥类型</param>
        /// <param name="MeterRand">表随机数</param>
        /// <param name="Meterid">表编号</param>
        /// <param name="MeterEsamSerial">表序列号</param>
        /// <param name="PutApdu">指令数据</param>
        /// <param name="PutData">输入明文</param>
        /// <param name="OutEndata">输出密文</param>
        /// <param name="OutMac">输出MAC</param>
        /// <returns></returns>
        public bool getParameterElseUpdate(EnumKeyState ks, string MeterRand, string Meterid, string MeterEsamSerial, string PutApdu, string PutData, out string OutEndata, out string OutMac)
        {
            OutEndata = OutMac = "";

            StringBuilder putData = new StringBuilder(PutData);

            StringBuilder outData = new StringBuilder(int.Parse(PutApdu.Substring(8), System.Globalization.NumberStyles.AllowHexSpecifier));  //分配数据大小

            StringBuilder rand = new StringBuilder(MeterRand);

            StringBuilder div = new StringBuilder(Meterid.PadLeft(16, '0'));

            StringBuilder putApdu = new StringBuilder(PutApdu);

            int intResult = 1;

            lock (this)
            {
#if PRIVATEKEY
                intResult = API.ParameterElseUpdate(ks == EnumKeyState.公钥状态 ? 0 : 1, rand, div, putApdu, putData, outData);
#else
                intResult = API.ParameterElseUpdate(0, rand, div, putApdu, putData, outData);
#endif

            }

            if (intResult != 0) return false;

            OutMac = outData.ToString().Substring(outData.Length - 8);      //后8位是MAC

            OutEndata = outData.ToString().Substring(0, outData.Length - 8);

            return true;

        }

        /// <summary>
        /// 一类参数设置
        /// </summary>
        /// <param name="ks">密钥类型</param>
        /// <param name="MeterRand">表随机数</param>
        /// <param name="Meterid">表号</param>
        /// <param name="MeterEsamSerial">表ESAM序列号</param>
        /// <param name="PutApdu">输入指令</param>
        /// <param name="PutData">输入数据</param>
        /// <param name="OutEndata">输出MAC</param>
        /// <returns></returns>
        public bool getParameterUpdate(EnumKeyState ks, string MeterRand, string Meterid, string MeterEsamSerial, string PutApdu, string PutData, out string OutEndata)
        {
            OutEndata = "";

            StringBuilder putData = new StringBuilder(PutData);

            StringBuilder outData = new StringBuilder(8);

            StringBuilder rand = new StringBuilder(MeterRand);

            StringBuilder div = ks == EnumKeyState.公钥状态 ? new StringBuilder("1".PadLeft(16, '0')) : new StringBuilder(Meterid.PadLeft(16, '0'));

            StringBuilder putApdu = new StringBuilder(PutApdu);

            int intResult = 1;

            lock (this)
            {
#if PRIVATEKEY

                int flag = ks == EnumKeyState.公钥状态 ? 0 : 1;

                switch (PutApdu.Substring(4, 2))
                {
                    case "82":
                        intResult = API.ParameterUpdate(flag, rand, div, putApdu, putData, outData);
                        break;
                    case "83":
                        intResult = API.ParameterUpdate1(flag, rand, div, putApdu, putData, outData);
                        break;
                    case "84":
                        intResult = API.ParameterUpdate2(flag, rand, div, putApdu, putData, outData);
                        break;
                }
#else
                return false;
#endif
            }

            if (intResult != 0) return false;
            OutEndata = outData.ToString().Replace("\0", "");
            return true;

        }


        /// <summary>
        /// 费控功能控制
        /// </summary>
        /// <param name="Uc">控制类型</param>
        /// <param name="MeterDate">电能表当前时间</param>>
        /// <param name="MeterRand">表随机数</param>
        /// <param name="MeterEsamSerial">表ESAM序列号</param>
        /// <param name="OutData">输出密文</param>
        /// <returns></returns>
        public bool getUserControl(EnumKeyState ks, EnumUserControl Uc, DateTime MeterDate, string MeterRand, string MeterEsamSerial, out string OutData)
        {
            OutData = "";

            StringBuilder putData = new StringBuilder(string.Format("{0:X2}00{1:yyMMddHHmmss}", (int)Uc, MeterDate.AddHours(1)));
            StringBuilder outData = new StringBuilder(80);
            StringBuilder rand = new StringBuilder(MeterRand);
            StringBuilder div = new StringBuilder(CONST_PUBLICDVI);
            StringBuilder esam = new StringBuilder(MeterEsamSerial);

            int result = 1;

            lock (this)
            {
                result = API.UserControl(ks == EnumKeyState.公钥状态 ? 0 : 1, rand, div, esam, putData, outData);
            }

            if (result != 0) return false;

            OutData = outData.ToString();

            return true;

        }

        /// <summary>
        /// 获取下装密文
        /// </summary>
        /// <param name="meterID">表号</param>
        /// <param name="MeterRand">表随机数</param>
        /// <param name="MeterEsamSerial">表ESAM序列号</param>
        /// <param name="OutData">返回密钥信息，字典的数据部分结构为 KeyData|KeyInfo|KeyMac(密文|密钥信息|下装MAC)</param>
        /// <returns></returns>
        public bool getUpdateKey(string meterID, string MeterRand, string MeterEsamSerial, out Dictionary<EnumKeyNo, string> OutData)
        {
            OutData = new Dictionary<EnumKeyNo, string>();

            const string MeterKeyInfo1 = "01010401";//主控

            const string MeterKeyInfo2 = "01010202";//远程

            const string MeterKeyInfo3 = "01010103";//参数

            const string MeterKeyInfo4 = "01010304";//身份


            StringBuilder[] M = new StringBuilder[]{new StringBuilder(MeterKeyInfo1)//主控
                                                ,new StringBuilder(MeterKeyInfo2)//远程
                                                ,new StringBuilder(MeterKeyInfo3) //参数
                                                ,new StringBuilder(MeterKeyInfo4)};//身份

            StringBuilder[] outKey = new StringBuilder[] { new StringBuilder(64)
                                                         , new StringBuilder(64)
                                                         , new StringBuilder(64)
                                                         , new StringBuilder(64)};

            StringBuilder[] outKeyMac = new StringBuilder[] {new StringBuilder(8)
                                                         , new StringBuilder(8)
                                                         , new StringBuilder(8)
                                                         , new StringBuilder(8)};

            StringBuilder sMeterRand = new StringBuilder(MeterRand);

            StringBuilder sDvi = new StringBuilder(meterID.PadLeft(16, '0'));

            StringBuilder sMeterEsam = new StringBuilder(MeterEsamSerial);

            int result = 1;

            lock (this)
            {
                result = API.KeyUpdate(0, sMeterRand, sDvi, sMeterEsam,
                                                     M[0], M[1], M[2], M[3],
                                                     outKey[0], outKeyMac[0], outKey[1], outKeyMac[1], outKey[2], outKeyMac[2], outKey[3], outKeyMac[3]);
            }

            if (result != 0) return false;

            OutData.Add(EnumKeyNo.主控密钥, string.Format("{0}|{1}|{2}", outKey[0], M[0], outKeyMac[0]));
            OutData.Add(EnumKeyNo.远程控制密钥, string.Format("{0}|{1}|{2}", outKey[1], M[1], outKeyMac[1]));
            OutData.Add(EnumKeyNo.参数更新密钥, string.Format("{0}|{1}|{2}", outKey[2], M[2], outKeyMac[2]));
            OutData.Add(EnumKeyNo.身份认证密钥, string.Format("{0}|{1}|{2}", outKey[3], M[3], outKeyMac[3]));

            return true;

        }
        #region IDisposable 成员

        public void Dispose()
        {
            if (objectCount == 0) return;
            this.LgoutMachine();

            objectCount--;
        }

        #endregion
    }
}
